﻿using System;
using System.Drawing;
using System.Windows.Forms;
using RGBGame.VisualJSApp;
using VisualJS.Web.Forms;
using MessageBox = VisualJS.Web.Forms.MessageBox;
using Panel = VisualJS.Web.Forms.Panel;
using PictureBox = VisualJS.Web.Forms.PictureBox;

namespace RGBGame
{
    public class RGBGame:GameLogic
    {
        internal MainForm gameForm;

        public RGBGame(MainForm frm)
        {
            rgbSub = this;
            gameForm = frm;
        }

        internal void resetGame()
        {
            cellsFilled = 0;
            clearTiles();
            gameId = -1;
            gameHost.Enabled = false;
            opponentsId = "";
            myScore = 0;
            opScore = 0;
            updateScores();
        }

        internal void clearTiles()
        {
            for (int x = 0; x < 6; x++)
                for (int y = 0; y < 6; y++)
                    tileStatus[x][y] = Colors.Transparent;

            int i = 36;
            while (i-- > 0)
            {
                tiles[i].BackColor = Color.Transparent;
                tiles[i].Opacity = 100;
            }
        }

        internal PictureBox[] tiles = new PictureBox[36];
        internal Panel gameHost = new Panel();
        internal int cellsFilled = 0;
        internal string opponentsId = "";
        internal long gameId = -1;
        internal int w, h;
        internal bool rendered = false;
        
        internal void updatePlayerColors()
        {
            gameForm.lblMyColor.BackColor = Utility.getColor(MyColor);
            gameForm.lblOpColor.BackColor = Utility.getColor(OpColor);
        }

        public void GameEvent(NotificationReason reason, object gameData)
        {
            switch (reason)
            {
                case NotificationReason.JoinGame:
                    {
                        object[] data = (object[])gameData;
                        opponentsId = data[0].ToString();
                        gameId = (long)data[1];
                        int order = (int)data[2];
                        myTurn = order;
                        if (order == 1)
                            opTurn = 2;
                        else
                        {
                            opTurn = 1;
                        }

                        updatePlayerColors();
                        highlightInfo("Game has started<br/>Waiting for other player to play..");
                    }
                    break;
                case NotificationReason.YourTurn:
                    {
                        if (gameData != null)
                        {
                            object[] data = (object[])gameData;
                            int tileId = (int)data[0];

                            opTurn = (int)data[1];//current turn of opponent
                            markTile(tileId, OpColor);

                            opTurn = (int)data[2];//next turn of opponent
                            updatePlayerColors();
                        }

                        gameHost.Enabled = true;
                        hideInfo();
                        break;
                    }
                case NotificationReason.EndGame:
                case NotificationReason.EndGameOpponentExit:
                    {
                        bool crossSession = (bool)gameData;

                        resetGame();

                        if (!crossSession)
                        {
                            highlightInfo("The player left the game<br/>Waiting for other player");
                            Room.AddPlayer(gameForm.ClientID, GameEvent);
                        }
                        else
                        {
                            VisualJS.Web.Forms.MessageBox.Show("Warning!", "You have opened a new game on same browser instance!<br/>When you click OK the other instance will be closed.", gameForm, delegate(MessageBoxResult result)
                            {
                                Utility.BindSession(gameForm.SessionID, gameForm.ClientID);
                                highlightInfo("The player left the game<br/>Waiting for other player");
                                Room.AddPlayer(gameForm.ClientID, GameEvent);
                            });
                        }
                    }
                    break;
                case NotificationReason.GotScore:
                    {
                        object[] data = (object[])gameData;
                        opTurn = (int)data[2];
                        paintScore((int)data[0], (int)data[1], false, OpColor);
                        highlightScore("Your opponent got 1 point!", false);
                    }
                    break;
            }
        }

        public void AIGameEvent(NotificationReason reason, object gameData)
        {
            switch (reason)
            {
                case NotificationReason.JoinGame:
                    {
                        object[] data = (object[])gameData;
                        opponentsId = data[0].ToString();
                        gameId = (long)data[1];
                        int order = (int)data[2];
                        myTurn = order;
                        if (order == 1)
                            opTurn = 2;
                        else
                        {
                            opTurn = 1;
                        }

                        updatePlayerColors();
                        highlightInfo("Game has started<br/>Waiting for other player to play..");
                    }
                    break;
                case NotificationReason.YourTurn:
                    {
                        if (gameData != null)
                        {
                            object[] data = (object[])gameData;
                            int tileId = (int)data[0];

                            opTurn = (int)data[1];//current turn of opponent
                            markTile(tileId, OpColor);

                            opTurn = (int)data[2];//next turn of opponent
                            updatePlayerColors();
                        }

                        gameHost.Enabled = true;
                        hideInfo();
                        break;
                    }
                case NotificationReason.EndGame:
                case NotificationReason.EndGameOpponentExit:
                    {
                        bool crossSession = (bool)gameData;

                        resetGame();

                        if (!crossSession)
                        {
                            highlightInfo("The player left the game<br/>Waiting for other player");
                            Room.AddPlayer(gameForm.ClientID, GameEvent);
                        }
                        else
                        {
                            VisualJS.Web.Forms.MessageBox.Show("Warning!", "You have opened a new game on same browser instance!<br/>When you click OK the other instance will be closed.", gameForm, delegate(MessageBoxResult result)
                            {
                                Utility.BindSession(gameForm.SessionID, gameForm.ClientID);
                                highlightInfo("The player left the game<br/>Waiting for other player");
                                Room.AddPlayer(gameForm.ClientID, GameEvent);
                            });
                        }
                    }
                    break;
                case NotificationReason.GotScore:
                    {
                        object[] data = (object[])gameData;
                        opTurn = (int)data[2];
                        paintScore((int)data[0], (int)data[1], false, OpColor);
                        highlightScore("Your opponent got 1 point!", false);
                    }
                    break;
            }
        }

        internal void MouseDown(object sender, MouseEventArgs e)
        {
            if (!gameHost.Enabled)
                return;

            PictureBox box = (PictureBox)sender;

            //access control names from NameTip (box_0 ...) 
            //(VisualJS converts original names to numbers in order reduce client script size and improve security)
            int i = int.Parse(box.NameTip.Split('_')[1]);
            TilePoint xy = TilePoint.get(i);
            Colors ch = tileStatus[xy.X][xy.Y];
            if (ch == Colors.Transparent)
            {
                playTile(i);
            }
        }

        internal void playTile(int i)
        {
            markTile(i, MyColor);
            int result = checkScore(i, MyColor);

            if (result != CELL_NO_MATCH)
            {
                paintScore(result, i, true, MyColor);
                Room.GotScore(gameForm.ClientID, opponentsId, gameId, i, result, myTurn, GameEvent);
                highlightScore("You got 1 Point!<br/>Waiting for other player to play", true);
            }
            else
                highlightInfo("Waiting for other player to play");

            if(rendered)
                gameHost.Enabled = false;

            int nextTurn = Utility.getRandom();//get the next turn index
            Room.EndOfTurn(gameForm.ClientID, opponentsId, gameId, i, myTurn, nextTurn, GameEvent);

            myTurn = nextTurn;//activate it
            updatePlayerColors();
        }

        internal void gameEndCheck()
        {
            if (cellsFilled == 36)
            {
                string strGameResult = "You win the game!";
                if (opScore > myScore)
                    strGameResult = "You lost the game.";
                else if (opScore == myScore)
                {
                    strGameResult = "Draw! Nobody won the game.";
                }

                if (rendered)
                {
                    MessageBox.Show("Game Over!", strGameResult + "<br/>Would you like to play again?", gameForm, MessageBoxType.YesNo, delegate(MessageBoxResult result)
                    {
                        if (result == MessageBoxResult.Yes)
                        {
                            resetGame();
                            Room.ResetGame(gameForm.ClientID, GameEvent);
                            highlightInfo("You've back in the queue for a next game");
                        }
                        else
                        {
                            highlightInfo("Thanks for playing!");
                        }
                    });
                }
            }
        }

        internal void updateSizes()
        {
            w = gameForm.Width - 10;
            w -= 25; // 5 per each empty space or border
            w /= 6;
            h = gameForm.Height - 35;
            h -= 25; // 5 per each empty space or border
            h /= 6;
        }

        internal void DrawTiles()
        {
            gameHost.Name = "gameHost";
            gameHost.Location = new Point(0, 30);
            gameHost.Dock = DockStyle.Fill;

            updateSizes();

            for (int x = 0; x < 6; x++)
            {
                tileStatus[x] = new Colors[6];
            }

            int i = 36;
            while (i-- > 0)
            {
                tiles[i] = new PictureBox();
                tiles[i].Name = "box_" + i.ToString();
                locateTile(i);
                tiles[i].BorderStyle = BorderStyle.FixedSingle;
                tiles[i].BorderLineStyle = Panel.ClientBorderStyle.Dotted;
                tiles[i].BorderColor = "#999999".ToColor();
                tiles[i].MouseDown += new MouseEventHandler(MouseDown);
                gameHost.Controls.Add(tiles[i]);
            }
            rendered = true;

            gameHost.Enabled = false;
            gameForm.mainPanel.Controls.Add(gameHost);
        }

        internal void locateTile(int i)
        {
            if (!rendered)
                return;

            TilePoint xy = TilePoint.get(i);
            tiles[i].Location = new Point(5 + (xy.X * (w + 5)), 30 + (xy.Y * (h + 5)));
            tiles[i].Size = new Size(w, h);
        }

        internal bool waitForHide = false;
        internal void highlightScore(string text, bool self)
        {
            if(!rendered)
                return; 

            gameForm.pnlInfo.Visible = true;
            gameForm.lblKeep.Text = text;
            if (self)
            {
                gameForm.pnlInfo.BackColor = Color.YellowGreen;
                gameForm.pnlInfo.BackColorEnd = Color.Gold;
            }
            else
            {
                gameForm.pnlInfo.BackColor = "#a00000".ToColor();
                gameForm.pnlInfo.BackColorEnd = Color.Transparent;
                waitForHide = true;//wait some time to show other player got score!
            }
        }

        internal void highlightInfo(string text)
        {
            if (!rendered)
                return;

            gameForm.pnlInfo.BackColor = "#222222".ToColor();
            gameForm.pnlInfo.BackColorEnd = Color.Transparent;
            gameForm.pnlInfo.Visible = true;
            gameForm.lblKeep.Text = text;
        }

        internal void hideInfo()
        {
            if (!rendered)
                return;

            if (waitForHide)
            {
                gameForm.pnlInfo.HideAfterWait(1500);
                waitForHide = false;
            }
            else
            {
                gameForm.pnlInfo.Visible = false;
            }
        }

        internal void paintScore(int result, int i, bool self, Colors ch)
        {
            if (rendered)
            {
                Color clr = Utility.getColor(ch);
                int[] ids = GameLogic.scoreCells(i, result);

                for (int j = 0; j < 3; j++)
                {
                    tiles[ids[j]].Opacity = 20;
                    tiles[ids[j]].BackColor = clr;
                    setScore(ids[j], self, j == 0);
                }
            }
            else
            {
                setScore(i, self, true);
            }
        }

        internal void setScore(int i, bool self, bool increase = false)
        {
            TilePoint pos = TilePoint.get(i);
            if (self)
            {
                tileStatus[pos.X][pos.Y] = Colors.ScoredYou;
                if (increase) myScore++;
            }
            else
            {
                tileStatus[pos.X][pos.Y] = Colors.ScoredOpponent;
                if (increase) opScore++;
            }

            if (increase)
            {
                updateScores();
            }
        }

        internal void updateScores()
        {
            if (!rendered)
                return;

            gameForm.lbScore1.Text = myScore.ToString("D2");
            gameForm.lbScore2.Text = opScore.ToString("D2");
        }
    }
}